#ifndef _CROOT_H_
#define _CROOT_H_

#include "auto.h"
#include <buola/gui/caccelerator.h>
#include <buola/gui/x11/cglx.h>
#include <buola/gl/ctexture.h>
#include <buola/gl/copenglgc.h>
#include <buola/utility/cfpsmeasure.h>
#include <buola/app/ceventloop.h>
#include <buola/gui/x11/cx11window.h>
#include <buola/gui/x11/cglxcontext.h>

namespace buola { namespace bwm {

///////////////////////////////////////////////////////
///////////////////////////////////////////////////////
////////////////////// CRoot //////////////////////////
///////////////////////////////////////////////////////
///////////////////////////////////////////////////////

class CRoot : public CLoopFunction
{
    struct STimes
    {
        CMonotonicTime mCurrent;
        CMonotonicTime mPrevious;
        CMonotonicTime::duration mElapsed;
    };

///////////////////////////////
/////////// methods ///////////
///////////////////////////////

public:
//////////////
// creation
//
    CRoot();
    ~CRoot();

protected:
    void InitScreen();
    void InitWindows();
    void InitComposite();

    void SetDesktopRenderer(CDesktopRenderer *pRenderer);
    void SetSceneRenderer(CSceneRenderer *pRenderer);
public:    
    CDesktopRenderer *GetDesktopRenderer()  {   return mDesktopRenderer;    }
    CSceneRenderer *GetSceneRenderer()  {   return mSceneRenderer;  }
    const gl::CTexture &GetShadowTexture()  {   return mShadowTexture;  }

protected:
    void ShowOutputWindow();
    void HideOutputWindow();

//init functions
    void InitGLX();
    void InitGL();

    bool IsSpecialWindow(Window pID)    {   return (pID==mOverlayWindow->GetX11ID())||GetDecoration(pID);   }

    void WaitForVSync();

///////////////////////
// client management
//
public:
    CClient *GetClient(Window pID);
    CManagedClient *GetManagedClient(Window pID);
    CDecoration *GetDecoration(Window pID);
    CClient *CreateClientFor(Window pID,bool pWMStarting);

////////////////////////////
// root window properties
//
//functions to set root window properties
    //_NET_NUMBER_OF_DESKTOPS,_NET_DESKTOP_GEOMETRY
    //_NET_DESKTOP_VIEWPORT,_NET_WORKAREA
    void NETSetDesktopInfo();
    //_NET_DESKTOP_NAMES
    void NETSetDesktopNames();
    //_NET_CURRENT_DESKTOP
    void NETSetCurrentDesktop();
    //_NET_CLIENT_LIST
    void NETSetClientList();
    //_NET_CLIENT_LIST_STACKING
    void NETSetStackingList();

    void BuolaSetWMExtensions();

///////////////////////
// window management
//
//desktop
    void CreateDesktop(const std::wstring&,const CRect_i& =CRect_i(-1,-1,-1,-1),
                                const CPoint_i& =CPoint_i(0,0));
    void SwitchToDesktop(int pDesktop,bool pAnimate);

//activation
    void WMCirculate(bool pIconic);
    void WMRevert();
    void WMActivate(CManagedClient*,bool=true);
    CManagedClient *GetActiveClient()          {   return mActiveClient;   }
    unsigned int GetActiveDesktop() {   return mDesktops.mActive;   }
    int GetDesktopCount()           {   return mDesktops.mCount;    }
    const std::wstring &GetDesktopName(int i)   {   return mDesktops.mName[i];  }
    bool IsDesktopVisible(int i)    {   return mDesktops.mVisible[i];   }
    CPoint_i GetVirtualViewportOffset(int pFrom,int pTo)
    {
        CPoint_i lPoint=mDesktops.mVirtualViewport[pFrom]-
                        mDesktops.mVirtualViewport[pTo];
        if(lPoint.x>mDesktops.mWholeViewport.x/2)
            lPoint.x-=mDesktops.mWholeViewport.x;
        if(lPoint.x<-mDesktops.mWholeViewport.x/2)
            lPoint.x+=mDesktops.mWholeViewport.x;
        if(lPoint.y>mDesktops.mWholeViewport.y/2)
            lPoint.y-=mDesktops.mWholeViewport.y;
        if(lPoint.y<-mDesktops.mWholeViewport.y/2)
            lPoint.y+=mDesktops.mWholeViewport.y;

        return lPoint;
    }

    bool IsStarted()                {   return mStarted;                    }

    const CRect_i &GetWorkArea(unsigned int pNum=0xffffffff)
    {
        if(pNum==0xffffffff)
            pNum=GetActiveDesktop();
        return mDesktops.mWorkArea[pNum];
    }

//strut
    void WMIncStrut(int,int,int,int);

//order
    void WMRestack();
    void WMRaise(CManagedClient*);
    void WMLower(CManagedClient*);

    double GetFPS() {   return mFPS.Get();  }
    
    CSize_d GetSize() {   return mX11Window->GetSize();   }
    
public:
////////////
// damage
//
    void DamageViewport(const CRegion &pRegion)
    {
        if(GetFlag(COMP_ALLDAMAGE)) return;
        if(pRegion.empty()) return;
        SetFlag(COMP_VIEWPORTDAMAGE);
        mRegions.mViewportDamage|=pRegion;
    }

    void DamageDesktop(const CRegion &pRegion,int pDesktop)
    {
        if(GetFlag(COMP_ALLDAMAGE)) return;
        if(pRegion.empty()) return;
        if(!mDesktops.mVisible[pDesktop]) return;
        SetFlag(COMP_DESKTOPDAMAGE);
        mRegions.mDamage[pDesktop]|=pRegion;
    }

    void DamageAll()
    {
        SetFlag(COMP_ALLDAMAGE);
    }

protected:
/////////////////////
// composite flags
//
    void SetFlags(ERootFlags pFlags)    {   mFlags=pFlags;                      }
    ERootFlags GetFlags()               {   return mFlags;                      }

    void SetFlag(ERootFlags pFlag)      {   mFlags|=pFlag;                      }
    void UnsetFlag(ERootFlags pFlag)    {   mFlags&=(~pFlag);                   }

public:
    bool GetFlag(ERootFlags pFlag)      {   return ((mFlags&pFlag)==pFlag); }

    const STimes &GetTime()         {   return mTime;   }
    Window GetID()                  {   return mID;     }

////////////////////
// event handling
//
public:
    void OnKeyDown(const gui::CKeyEvent &pE);
    void OnKeyUp(const gui::CKeyEvent &pE);
    void OnClientRemoved(CClient *pClient);
    void OnDesktopsChanged();

    virtual bool OnLoopFunction();
    void OnComposeTimer();
    void OnBackgroundPropChanged();
    void OnRootExpose(XExposeEvent*);
    void OnRootConfigure();
    void OnRestack();
    void OnExpose(const gui::CExposeEvent &pE);
    void OnOutFinished();

////////////////////////////
/////////// data ///////////
////////////////////////////

public:
    int AcquireClientDataSlot(void*);
    void *ReleaseClientDataSlot(int);

///////////////////////
// event and clients
//
public:
    CEventManager *mEventManager;
    std::vector<CClient*> mClients;
    std::vector<CClient*> mClientsStack;
    std::vector<CManagedClient*> mManagedClients;

protected:
///////////////////////////
// window manager data
//
    ERootFlags mFlags;

protected:
    Window mID;
    bool mStarted;

    CManagedClient *mActiveClient;
    std::vector<CManagedClient*> mRevertStack;

    chrono::milliseconds mRedrawTime;
    chrono::milliseconds mOptimalRedrawTime;

    int mScreenNumber;
    int mDefaultDepth;

    CTimer *mTimer;

    struct
    {
        int mCount;                     //number of desktops
        int mActive;                    //index of active one
        double mAngleCovered;           //angle covered by each one
        double mDistance;               //distance to center of desktops
                                        //polygon

        std::wstring mName[MAX_DESKTOPS];   //names
        CRect_i mWorkArea[MAX_DESKTOPS];  //work area for each desktop
        CPoint_i mViewport[MAX_DESKTOPS]; //0,0
        CPoint_i mVirtualViewport[MAX_DESKTOPS];
                                        //for window translation
        CSize_i mWholeViewport;
        double mAngle[MAX_DESKTOPS];    //angle each desktop is at
        CPoint_i mPos[MAX_DESKTOPS];      //position of each desktop
                                        //relative to the first one
        bool mVisible[MAX_DESKTOPS];    //desktop is visible from current
                                        //view point
    } mDesktops;

    gui::CAccelerator mAltTab;
    gui::CAccelerator mAltF4;
    gui::CAccelerator mAltShiftTab;
    gui::CAccelerator mCtrlAltRight;
    gui::CAccelerator mCtrlAltLeft;
    gui::CAccelerator mWinS;
    gui::CAccelerator mWinD;

    gui::x11::CX11Window *mSupportingWMCheckWindow;

protected:
///////////////////////////
// window manager data
//
protected:
    gui::x11::CGLXContext *mGLContext;
    gui::x11::CX11Window *mOverlayWindow;

    struct
    {
        CRegion mDesktop;
        CRegion mRootExpose;
        CRegion mDamage[MAX_DESKTOPS];
        CRegion mViewportDamage;
        CRegion mPainted;
    } mRegions;

protected:
    gui::x11::CX11Window *mX11Window;
    gl::CTexture mShadowTexture;

    CSceneRenderer *mSceneRenderer;
    CDesktopRenderer *mDesktopRenderer;

    STimes mTime;

    struct
    {
        std::vector<void*> mSlots;
        int mNumSlots;
    } mClientData;

    CCubeRenderer *mCubeRenderer;
    CShuffleRenderer *mShuffleRenderer;
    CDefaultDesktopRenderer *mDefaultDesktopRenderer;

    bool mGoingOut;

    CFPSMeasure mFPS;
    bool mShowStatistics;
    bool mUseDepthTest;
    
    friend class CDesktopRenderer;
    friend class CSceneRenderer;
    friend class CCubeRenderer;
    friend class CDefaultDesktopRenderer;
    friend class CEventManager;
    friend class CShuffleRenderer;
};

extern CRoot *gRoot;

/*namespace bwm*/ } /*namespace buola*/ }

#endif
